package spark.math;

public class Vector3 {
	public float x;
	public float y;
	public float z;

	public static final int SIZE = Float.SIZE * 3;

	public Vector3() {
	}

	public Vector3(float x, float y, float z) {
		this.x = x;
		this.y = y;
		this.z = z;
	}

	public Vector3(final Vector3 source) {
		this.x = source.x;
		this.y = source.y;
		this.z = source.z;
	}

	public Vector3(final Vector2 source) {
		this.x = source.x;
		this.y = source.y;
	}

	public static Vector3 cross(final Vector3 aLeft, final Vector3 aRight) {
		return new Vector3(
				aLeft.y * aRight.z - aLeft.z * aRight.y,
				aLeft.z * aRight.x - aLeft.x * aRight.z,
				aLeft.x * aRight.y - aLeft.y * aRight.x);
	}

	public static float dot(final Vector3 aLeft, final Vector3 aRight) {
		return
				aLeft.x * aRight.x +
				aLeft.y * aRight.y +
				aLeft.z * aRight.z;
	}

	public static Vector3 normalize(final Vector3 aVector) {
		Vector3 normalizedVector = new Vector3(aVector);
		normalizedVector.normalize();
		return normalizedVector;
	}

	public Vector3 normalize() {
		float length = this.length();

		if(length > 0) {
			x /= length;
			y /= length;
			z /= length;
		}
		return this;
	}

	public static float length(final Vector3 aVector) {
		return (float) Math.sqrt(
				aVector.x * aVector.x +
				aVector.y * aVector.y +
				aVector.z * aVector.z);
	}

	public float length() {
		return length(this);
	}

	public static float lengthWithoutSquareRoot(final Vector3 aVector) {
		return
				aVector.x * aVector.x +
				aVector.y * aVector.y +
				aVector.z * aVector.z;
	}

	public float lengthWithoutSquareRoot() {
		return lengthWithoutSquareRoot(this);
	}

	public static Vector3 transformCoordinate(final Vector3 aSource, final Matrix aSourceMatrix) {
		Vector3 newVector = new Vector3(aSource);
		newVector.transformCoordinate(aSourceMatrix);
		return newVector;
	}

	public Vector3 transformCoordinate(final Matrix aSourceMatrix) {
		Vector4 transformed = new Vector4(x, y, z, 1.0f);
		transformed.transform(aSourceMatrix);

		x = transformed.x / transformed.w;
		y = transformed.y / transformed.w;
		z = transformed.z / transformed.w;
		return this;
	}

	public static Vector3 transformNormal(final Vector3 aSource, final Matrix aSourceMatrix) {
		Vector3 newVector = new Vector3(aSource);
		newVector.transformNormal(aSourceMatrix);
		return newVector;
	}

	public Vector3 transformNormal(final Matrix aSourceMatrix) {
		Vector4 transformed = new Vector4(x, y, z, 0.0f);
		transformed.transform(aSourceMatrix);

		x = transformed.x;
		y = transformed.y;
		z = transformed.z;
		return this;
	}

	public static Vector3 lerp(final Vector3 aLeft, final Vector3 aRight, float aInterpolater) {
		return Vector3.add(Vector3.multiply(aLeft, 1.0f - aInterpolater), Vector3.multiply(aRight, aInterpolater));
	}

	public static Vector3 add(final Vector3 left, final Vector3 right) {
		return new Vector3(left.x + right.x, left.y + right.y, left.z + right.z);
	}

	public Vector3 add(final Vector3 vector) {
		this.x += vector.x;
		this.y += vector.y;
		this.z += vector.z;
		return this;
	}

	public static Vector3 substract(final Vector3 left, final Vector3 right) {
		return new Vector3(left.x - right.x, left.y - right.y, left.z - right.z);
	}

	public Vector3 substract(final Vector3 vector) {
		this.x -= vector.x;
		this.y -= vector.y;
		this.z -= vector.z;
		return this;
	}

	public static Vector3 multiply(final Vector3 vector, final float scalar) {
		return new Vector3(vector.x * scalar, vector.y * scalar, vector.z * scalar);
	}

	public Vector3 multiply(final float scalar) {
		this.x *= scalar;
		this.y *= scalar;
		this.z *= scalar;
		return this;
	}

	public static Vector3 multiply(final Vector3 left, final Vector3 right) {
		return new Vector3(left.x * right.x, left.y * right.y, left.z * right.z);
	}

	public Vector3 multiply(final Vector3 vector) {
		this.x *= vector.x;
		this.y *= vector.y;
		this.z *= vector.z;
		return this;
	}

	public static Vector3 divide(final Vector3 vector, final float scalar) {
		return new Vector3(vector.x / scalar, vector.y / scalar, vector.z / scalar);
	}

	public Vector3 divide(final float scalar) {
		this.x /= scalar;
		this.y /= scalar;
		this.z /= scalar;
		return this;
	}

	public static void copy(Vector3 target, Vector2 source) {
		target.x = source.x;
		target.y = source.y;
	}

	public Vector3 copy(Vector2 vector) {
		this.x = vector.x;
		this.y = vector.y;
		return this;
	}

	public static void copy(Vector3 target, Vector3 source) {
		target.x = source.x;
		target.y = source.y;
		target.z = source.z;
	}

	public Vector3 copy(Vector3 vector) {
		this.x = vector.x;
		this.y = vector.y;
		this.z = vector.z;
		return this;
	}

	@Override
	public String toString() {
		return "X(" + x + ") Y(" + y + ") Z(" + z;
	}
}
